import { shakeIt } from '@/utils/common';
import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react'
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
import Spinning from '../Spinning/Spinning';
import styles from './Modal.less'

interface IDModal {
  title?: string | ReactNode
  content: string | ReactNode
  cancelText?: string | ReactNode
  confirmText?: string | ReactNode
  afterClose?: () => void
  mask?: boolean
  maskClose?: boolean
  maskClass?: string
  footer?: ReactNode
  wrapClass?: string
  headClass?: string
  bodyClass?: string
  footClass?: string
  onCancel?: () => (Promise<any> | void)
  onConfirm?: (res?: string) => (Promise<any> | void)
  defaultValue?: string
  placeholder?: string
  shake?: boolean
  maxLength?: number
}

let modalWrap: HTMLDivElement;

/* 初始化外部容器 */
const initWrap = () => {
  if (!modalWrap) {
    modalWrap = document.createElement('div');
    document.body.appendChild(modalWrap);
  }
  modalWrap.className = styles.modal_wrap;
  const modalBox = document.createElement('div');
  modalWrap.appendChild(modalBox);
  return modalBox;
};

/** 判斷是否為promise */
function isPromise(obj: any) {

  return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';

}

function unmountFn(elemNode: Element, modalWrap: HTMLDivElement) {
  if (elemNode && modalWrap) {
    unmountComponentAtNode(elemNode);
    modalWrap.removeChild(elemNode);
  }
}

interface IDMMain extends IDModal {
  type: 'alert' | 'confirm' | 'prompt' | 'wrapper'
  wrapNode: HTMLDivElement
  elemNode: Element | DocumentFragment;
}

const ModalMain: React.FC<IDMMain> = props => {
  let unmounted = false;
  const { type, elemNode, wrapNode, } = props;
  const { title, content, cancelText, confirmText, defaultValue, placeholder, maxLength } = props;
  const { onCancel, onConfirm, afterClose, shake } = props;
  const { mask, maskClose, maskClass } = props
  const { wrapClass, headClass, bodyClass, footClass, footer } = props
  const [loading, setLoading] = useState<boolean>(false);
  const inputRef = useRef<HTMLInputElement>(null);
  const [promptText, setPromptText] = useState(defaultValue)

  // 卸载
  const unmount = useMemo(() => {
    return () => {
      if (elemNode && wrapNode) {
        unmounted = true
        unmountComponentAtNode(elemNode);
        wrapNode.removeChild(elemNode);
        afterClose && afterClose();
      }
    };
  }, [elemNode, wrapNode]);

  /** 文本輸入 */
  const inputPromptText = (event: React.ChangeEvent<HTMLInputElement>) => {
    setPromptText(event.target.value)
  }

  /** 確認回調 */
  const handleConfirm = () => {
    if (!onConfirm) {
      unmount();
      return false;
    }
    let confirmFn: any = onConfirm(type === 'prompt' ? promptText : undefined);
    if (isPromise(confirmFn)) {
      // setLoading(true)
      confirmFn.then((res: boolean) => {
        res && shake && shakeIt(300);
        res && unmount();
      }).finally(() => {
        !unmounted && setLoading(false)
      })
    } else {
      unmount()
    }
  }

  /** 取消回調 */
  const handleCancel = () => {
    unmount();
    onCancel && onCancel();
  }

  useEffect(() => {
    inputRef.current && inputRef.current.focus();
  }, [])

  return (
    <>
      {mask && <div className={`${styles.modal_mask} ${maskClass}`} onClick={() => { maskClose && handleCancel() }}></div>}
      <div className={`${styles.modal_wrap} ${wrapClass}`}>
        {type === 'wrapper'
          ? content
          : (<div className={styles.modal_box}>
            <div className={`${styles.modal_header} ${headClass}`}>
              <h3 className={styles.title}>{title}</h3>
            </div>
            <div className={`${styles.modal_body} ${bodyClass}`}>
              {content}
              {type === 'prompt' && <div className={styles.input_wrap}>
                <input ref={inputRef} type="text" value={promptText} placeholder={placeholder} maxLength={maxLength} onChange={inputPromptText} />
              </div>}
            </div>
            {footer ? footer : <div className={`${styles.modal_footer} ${footClass}`}>
              {type !== 'alert' && <button className={styles.btn_cancel} onClick={handleCancel}>{cancelText}</button>}
              <button className={styles.btn_confirm} onClick={handleConfirm} disabled={loading}>
                {confirmText}
                {loading && <Spinning className={styles.confirm_loading} state="light" type='colors' />}
              </button>
            </div>}
          </div>)}
      </div>
    </>
  );
};

ModalMain.defaultProps = {
  title: '溫馨提示',
  cancelText: '取消',
  confirmText: '確認',
  mask: true,
  maskClose: false,
  maskClass: '',
  wrapClass: '',
  headClass: '',
  bodyClass: '',
  footClass: '',
  defaultValue: '',
  placeholder: '',
  maxLength: 60
}

/* 类型 - 提示 */
const alert = (params: IDModal) => {
  let elemNode = initWrap();
  modalWrap.className = styles.message_box;
  ReactDOM.render(
    <ModalMain {...params} type="alert" elemNode={elemNode} wrapNode={modalWrap} />,
    elemNode,
  );
  return {
    hide: () => { unmountFn(elemNode, modalWrap) }
  }
};

/* 类型 - 確認 */
const confirm = (params: IDModal) => {
  let elemNode = initWrap();
  modalWrap.className = styles.message_box;
  ReactDOM.render(
    <ModalMain {...params} type="confirm" elemNode={elemNode} wrapNode={modalWrap} />,
    elemNode,
  );
  return {
    hide: () => { unmountFn(elemNode, modalWrap) }
  }
};

/* 类型 - 確認 */
const prompt = (params: IDModal) => {
  let elemNode = initWrap();
  modalWrap.className = styles.message_box;
  ReactDOM.render(
    <ModalMain {...params} type="prompt" elemNode={elemNode} wrapNode={modalWrap} />,
    elemNode,
  );
  return {
    hide: () => { unmountFn(elemNode, modalWrap) }
  }
};

/* 类型 - 容器 */
const wrapper = (params: IDModal) => {
  let elemNode = initWrap();
  modalWrap.className = styles.message_box;
  ReactDOM.render(
    <ModalMain {...params} type="wrapper" elemNode={elemNode} wrapNode={modalWrap} />,
    elemNode,
  );
  return {
    hide: () => { unmountFn(elemNode, modalWrap) }
  }
};


export default {
  alert,
  confirm,
  prompt,
  wrapper
}
